package xgen;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import xdb.util.DatabaseMetaData;

import java.io.PrintStream;
import java.util.*;

public class XBean extends Type {
    private int maxjsonname;
    private final String name;
    private boolean anyMaybe = false;
    private final List<Variable> variables = new ArrayList<Variable>();
    private final List<Enum> enums = new ArrayList<Enum>();
    private boolean isData = false;
    private final Set<String> varNames = new HashSet<String>();
    private final Set<Integer> jsonNames = new HashSet<Integer>();
    ///////--------增加对Transform的支持. caijiacheng 2009-9-1------///
    private boolean hasModify = false;

    public XBean(Element self) {
        name = self.getAttribute("name").trim();
        Main.verifyName(name);
        if (name.equals("Pod")) {
            throw new RuntimeException("name of 'Pod' has been used by xdb");
        }
        maxjsonname = Integer.valueOf(self.getAttribute("maxjsonname").trim());
        if (maxjsonname == 0) {
            throw new IllegalArgumentException("Not Find maxjsonname " + "@" + name);
        }
        Type.add(this);
        anyMaybe = "true".equalsIgnoreCase(self.getAttribute("any"));

        NodeList childnodes = self.getChildNodes();
        for (int i = 0; i < childnodes.getLength(); ++i) {
            Node node = childnodes.item(i);
            if (Node.ELEMENT_NODE != node.getNodeType()) {
                continue;
            }

            Element e = (Element) node;
            String nodename = e.getNodeName();

            if (nodename.equals("variable")) {
                add(new Variable(e));
            } else if (nodename.equals("enum")) {
                enums.add(new Enum(e));
            } else {
                throw new RuntimeException("node=" + nodename);
            }
        }
    }

    /**
     * add by cjc
     * 通过DataBaseMetaData的信息的var type信息，重新生成一个新的xbean
     */
    public XBean(DatabaseMetaData.Bean db) {
        name = db.getName();
        Main.verifyName(name);
        if (name.equals("Pod")) {
            throw new RuntimeException("name of 'Pod' has been used by xdb");
        }
        Type.add(this);

        DatabaseMetaData.Bean be = DatabaseMetaData.getInstance().getBean(name);
        assert (be != null);
        anyMaybe = be.isAny();
        for (DatabaseMetaData.Bean.Variable vb : be.getVariables()) {
            add(new Variable(vb));
        }
    }

    static void make(Collection<XBean> beans) {
        // generate Pod
        String classname = "Pod";
        PrintStream ps = Main.openBeanFile(classname);
        ps.println();
        ps.println("package xbean;");
        ps.println();
        ps.println("import java.util.LinkedList;");
        ps.println("import xdb.Bean;");
        ps.println("import xdb.Pair;");
        ps.println();
        ps.println("public final class " + classname + " {");
        ps.println();
        ps.println("public static boolean useBeanPool = false;");
        ps.println();
        ps.println("	private static ThreadLocal<LinkedList<Pair<Bean, xdb.util.BeanPool<?>>>> pBeans = new ThreadLocal<LinkedList<Pair<Bean, xdb.util.BeanPool<?>>>>() {");
        ps.println("		@Override");
        ps.println("		protected LinkedList<Pair<Bean, xdb.util.BeanPool<?>>> initialValue() {");
        ps.println("			return new LinkedList<Pair<Bean, xdb.util.BeanPool<?>>>();");
        ps.println("		}");
        ps.println("	};");
        ps.println();
        ps.println("	public static void AddBeans(Bean b, xdb.util.BeanPool<?> bp) {");
        ps.println("		if(useBeanPool == false) {");
        ps.println("			return;");
        ps.println("		}");
        ps.println("		if(Thread.currentThread().getName().startsWith(\"xdb.Worker.protocol\") || Thread.currentThread().getName().startsWith(\"xdb.Worker.procedure\")) {");
        ps.println("			LinkedList<Pair<Bean, xdb.util.BeanPool<?>>> bps = pBeans.get();");
        ps.println("			bps.add(new Pair<Bean, xdb.util.BeanPool<?>>(b, bp));");
        ps.println("		}");
        ps.println("	}");
        ps.println();
        ps.println("	public static void ReuseBeans() {");
        ps.println("		LinkedList<Pair<Bean, xdb.util.BeanPool<?>>> bps = pBeans.get();");
        ps.println("		if(bps.size() > 0) {");
        ps.println("			pBeans.remove();");
        ps.println("			for (Pair<Bean, xdb.util.BeanPool<?>> e : bps) {");
        ps.println("				@SuppressWarnings(\"unchecked\")");
        ps.println("				final xdb.util.BeanPool<Bean> p = (xdb.util.BeanPool<Bean>)e.second;");
        ps.println("				p._reset_unsafe_add_(e.first);");
        ps.println("			}");
        ps.println("			bps.clear();");
        ps.println("		}");
        ps.println("	}");
        ps.println();

        for (XBean bean : beans) {
            final String bn = bean.getName();
            ps.println("	public static xdb.util.BeanPool<xbean.__." + bn + ".Data> pool" + bn + " = new xdb.util.BeanPool<xbean.__." + bn + ".Data>() {");
            ps.println("		@Override");
            ps.println("		protected xbean.__." + bn + ".Data newBean() {");
            ps.println("			return new xbean.__." + bn + ".Data();");
            ps.println("		}");
            ps.println("	};");
            ps.println();
            ps.println("	public static " + bn + " new" + bn + "() {");
            ps.println("		return new xbean.__." + bn + "();");
            ps.println("	}");
            ps.println();
            ps.println("	public static void _reset_unsafe_add_(xbean.__." + bn + ".Data bean) {");
            ps.println("		pool" + bn + "._reset_unsafe_add_(bean);");
            ps.println("	}");
            ps.println();
            ps.println("	public static " + bn + " new" + bn + "Data() {");
            ps.println("		" + bn + " tmp = pool" + bn + ".get();");
            ps.println("		AddBeans(tmp, xbean.Pod.pool" + bn + ");");
            ps.println("		return tmp;");
            ps.println("	}");
            ps.println();
        }

        ps.println("}");
        ps.close();
    }

    @Override
    public void addOwnerTable(Table owner) {
        super.addOwnerTable(owner);
        for (Variable var : variables) {
            var.getVartype().addOwnerTable(owner);
        }
    }

    private void add(Variable var) {
        if (false == varNames.add(var.getname())) {
            throw new IllegalArgumentException("duplicate varname: " + var.getname() + "@" + name);
        }
        if (jsonNames.contains(var.getjsonname())) {
            throw new IllegalArgumentException("duplicate jsonname: " + var.getjsonname() + "@" + name);
        }
        if (maxjsonname < Integer.valueOf(var.getjsonname())) {
            throw new IllegalArgumentException("invalid jsonname: " + var.getjsonname() + " maxjsonname:" + maxjsonname + "@" + name);
        }
        jsonNames.add(var.getjsonname());
        variables.add(var);
    }

    public boolean isData() {
        return isData;
    }

    @Override
    public Type compile(String key, String value) {
        if (key != null && !key.isEmpty()) {
            throw new RuntimeException(getName() + " DO NOT NEED A KEY!");
        }
        if (value != null && !value.isEmpty()) {
            throw new RuntimeException(getName() + " DO NOT NEED A VALUE!");
        }
        return this;
    }

    @Override
    public boolean isAny() {
        for (Type type : this.depends()) {
            if (type != this && type.isAny()) {
                return true;
            }
        }
        return false;
    }

    public void compile(Xdb xdb) {
        boolean old = Type.setAny(anyMaybe);
        try {
            for (Variable var : variables) {
                var.compile(xdb);
            }
        } finally {
            Type.setAny(old);
        }
    }

    public void verify(Xdb xdb) {
        for (Variable var : variables) {
            var.verify(xdb, this);
        }
    }

    @Override
    public void verifyCapacity(Xdb xdb, Capacity capacity) {
        capacity.notNeed();
    }

    private void generateImplementCopyConstruct(PrintStream ps) {
        String cls = getName();
        ps.println("	public " + cls + "(" + cls + " _o_) {");
        ps.println("		this(_o_, null, null);");
        ps.println("	}");
        ps.println();
        ps.println("	" + cls + "(xbean." + cls + " _o1_, xdb.XBean _xp_, String _vn_) {");
        ps.println("		super(_xp_, _vn_);");

        if (isAny()) {
            ps.println("		throw new UnsupportedOperationException();");
            ps.println("	}");
            ps.println();
            return;
        }

        ps.println("		if (_o1_ instanceof " + cls + ") {");
        ps.println("			assign((" + cls + ")_o1_);");
        ps.println("		} else if (_o1_ instanceof " + cls + ".Data) {");
        ps.println("			assign((" + cls + ".Data)_o1_);");
        ps.println("		} else if (_o1_ instanceof " + cls + ".Const) {");
        ps.println("			assign(((" + cls + ".Const)_o1_).nThis());");
        ps.println("		} else {");
        ps.println("			throw new UnsupportedOperationException();");
        ps.println("		}");
        ps.println("	}");
        ps.println();

        ps.println("	private void assign(" + cls + " _o_) {");
        Main._xdb_verify_(ps, "		_o_.");
        for (Variable var : variables) {
            var.deepCopy(this, ps, "		");
        }
        ps.println("	}");
        ps.println();

        ps.println("	private void assign(" + cls + ".Data _o_) {");
        for (Variable var : variables) {
            var.deepCopy(this, ps, "		");
        }
        ps.println("	}");
        ps.println();
    }

    private void generateImplement() {
        String cls = getName();
        PrintStream ps = Main.openBean__File(cls);

        ps.println();
        ps.println("package xbean.__;");
        ps.println();
        if (Main.isUseJson()) {
            if (isAny() == false) {
                ps.println("import java.io.StringReader;");
                ps.println("import java.io.StringWriter;");
            }
            ps.println("import com.google.gson.stream.JsonReader;");
            ps.println("import com.google.gson.stream.JsonWriter;");
        }
        ps.println("import com.goldhuman.Common.Marshal.OctetsStream;");
        ps.println("import com.goldhuman.Common.Marshal.MarshalException;");
        ps.println();
        ps.println("public final class " + cls + " extends xdb.XBean implements xbean." + cls + " {");

        // variable declare
        for (Variable var : variables) {
            var.declare(ps, "	");
        }
        ps.println();

        // _reset_unsafe_
        ps.println("	@Override");
        ps.println("	public void _reset_unsafe_() {");
        for (Variable var : variables) {
            var.getVartype().print4reset(this, var, ps, "		");
        }
        ps.println("	}");
        ps.println();

        // constructor initial. 为了明确区别于copy-constructor，增加一个 int 参数。
        // 这是由于parent是XBean,所有的自定义XBean也是XBean。防止调用上出现问题。
        ps.println("	" + cls + "(int __, xdb.XBean _xp_, String _vn_) {");
        ps.println("		super(_xp_, _vn_);");
        for (Variable var : variables) {
            var.construct(this, ps, "		");
        }
        ps.println("	}");
        ps.println();
        ps.println("	public " + cls + "() {");
        ps.println("		this(0, null, null);");
        ps.println("	}");
        ps.println();

        generateImplementCopyConstruct(ps);

        if (Main.isUseJson()) {
            // marshal implement
            ps.println("	@Override");
            ps.println("	public final OctetsStream marshal(OctetsStream _os_) {");
            if (isAny()) {
                ps.println("		throw new UnsupportedOperationException();");
            } else {
                Main._xdb_verify_(ps, "		");
                ps.println("		try {");
                ps.println("			StringWriter sw = new StringWriter();");
                ps.println("			JsonWriter _out_ = new JsonWriter(sw);");
                ps.println("			marshal(_out_);");
                ps.println("			_os_.marshalJson(sw.getBuffer().toString());");
                ps.println("			_out_.close();");
                ps.println("		} catch (Exception e) {");
                ps.println("			e.printStackTrace();");
                ps.println("		}");
                ps.println("		return _os_;");
            }
            ps.println("	}");
            ps.println();

            // marshal JsonWriter implement
            ps.println("	@Override");
            ps.println("	public final void marshal(JsonWriter _out_) throws Exception {");
            if (isAny()) {
                ps.println("		throw new UnsupportedOperationException();");
            } else {
                ps.println("		_out_.beginObject();");
                for (Variable var : variables) {
                    if (var.getVartype() instanceof XBean) {
                        ps.println("		_out_.name(\"" + var.getjsonname() + "\");");
                        ps.println("		" + var.getname() + ".marshal(_out_);");
                    } else {
                        ps.println("		if (" + var.defaultCheck() + ") {");
                        ps.println("			_out_.name(\"" + var.getjsonname() + "\");");
                        var.marshalJson(this, ps, "			", "this");
                        ps.println("		}");
                    }
                }
                ps.println("		_out_.endObject();");
            }
            ps.println("	}");
            ps.println();

            // unmarshal implement
            ps.println("	@Override");
            ps.println("	public final OctetsStream unmarshal(OctetsStream _os_) {");
            if (isAny()) {
                ps.println("		throw new UnsupportedOperationException();");
            } else {
                Main._xdb_verify_(ps, "		");
                ps.println("		try {");
                ps.println("			JsonReader jr = new JsonReader(new StringReader(_os_.unmarshalJson()));");
                ps.println("			unmarshal(jr);");
                ps.println("			jr.close();");
                ps.println("		} catch (Exception e) {");
                ps.println("			e.printStackTrace();");
                ps.println("		}");
                ps.println("		return _os_;");
            }
            ps.println("	}");
            ps.println();

            // unmarshal JsonReader implement
            ps.println("	@Override");
            ps.println("	public final void unmarshal(JsonReader _in_) throws Exception {");
            if (isAny()) {
                ps.println("		throw new UnsupportedOperationException();");
            } else {
                ps.println("		_in_.beginObject();");
                ps.println("		while (_in_.hasNext()) {");
                ps.println("			switch (_in_.nextName()) {");
                for (Variable var : variables) {
                    ps.println("				case \"" + var.getjsonname() + "\":");
                    if (var.getVartype() instanceof XBean) {
                        ps.println("					" + var.getname() + ".unmarshal(_in_);");
                    } else {
                        var.unmarshalJson(this, ps, "					", "this");
                    }
                    ps.println("					break;");
                }
                ps.println("				default:");
                ps.println("					_in_.skipValue();");
                ps.println("					break;");
                ps.println("			}");
                ps.println("		}");
                ps.println("		_in_.endObject();");
            }
            ps.println("	}");
            ps.println();
        } else {
            // marshal implement
            ps.println("	@Override");
            ps.println("	public final OctetsStream marshal(OctetsStream _os_) {");
            if (isAny()) {
                ps.println("		throw new UnsupportedOperationException();");
            } else {
                Main._xdb_verify_(ps, "		");
                ps.println("		_os_.compact_uint32(" + variables.size() + ");");
                for (Variable var : variables) {
                    var.marshal(this, ps, "		");
                }
                ps.println("		return _os_;");
            }
            ps.println("	}");
            ps.println();

            // unmarshal implement
            ps.println("	@Override");
            ps.println("	public final OctetsStream unmarshal(OctetsStream _os_) throws MarshalException {");
            if (isAny()) {
                ps.println("		throw new UnsupportedOperationException();");
            } else {
                Main._xdb_verify_(ps, "		");
                ps.println("		int _varIndex = 0;");
                ps.println("		int _varSize = _os_.uncompact_uint32();");
                for (Variable var : variables) {
                    ps.println("		if (_varIndex++ < _varSize) {");
                    var.unmarshal(this, ps, "			");
                    ps.println("		}");
                }
                ps.println("		return _os_;");
            }
            ps.println("	}");
            ps.println();
        }

        // copy toData toBean
        ps.println("	@Override");
        ps.println("	public xbean." + cls + " copy() {");
        Main._xdb_verify_(ps, "		");
        ps.println("		return new " + cls + "(this);");
        ps.println("	}");
        ps.println();
        ps.println("	@Override");
        ps.println("	public xbean." + cls + " toData() {");
        Main._xdb_verify_(ps, "		");
        if (isAny()) {
            ps.println("		return new Data(this);");
        } else {
            ps.println("		xbean.__." + cls + ".Data tmp = (Data) xbean.Pod.new" + cls + "Data();");
            ps.println("		tmp.assign(this);");
            ps.println("		return tmp;");
        }
        ps.println("	}");
        ps.println();
        ps.println("	public xbean." + cls + " toBean() {");
        Main._xdb_verify_(ps, "		");
        ps.println("		return new " + cls + "(this); // same as copy()");
        ps.println("	}");
        ps.println();
        ps.println("	@Override");
        ps.println("	public xbean." + cls + " toDataIf() {");
        Main._xdb_verify_(ps, "		");
        ps.println("		return new Data(this);");
        ps.println("	}");
        ps.println();
        ps.println("	public xbean." + cls + " toBeanIf() {");
        Main._xdb_verify_(ps, "		");
        ps.println("		return this;");
        ps.println("	}");
        ps.println();

        // const
        ps.println("	@Override");
        ps.println("	public xdb.Bean toConst() {");
        Main._xdb_verify_(ps, "		");
        ps.println("		return new Const();");
        ps.println("	}");
        ps.println();

        // getter setter
        for (Variable var : variables) {
            var.getter(this, ps, "	");
        }
        for (Variable var : variables) {
            var.setter(this, ps, "	");
        }

        // equals
        ps.println("	@Override");
        ps.println("	public final boolean equals(Object _o1_) {");
        Main._xdb_verify_(ps, "		");
        ps.println("		" + cls + " _o_ = null;");
        ps.println("		if ( _o1_ instanceof " + cls + " ) {");
        ps.println("			_o_ = (" + cls + ")_o1_;");
        ps.println("		} else if ( _o1_ instanceof " + cls + ".Const ) {");
        ps.println("			_o_ = ((" + cls + ".Const)_o1_).nThis();");
        ps.println("		} else {");
        ps.println("			return false;");
        ps.println("		}");

        for (Variable var : variables) {
            var.equals(ps, "		");
        }
        ps.println("		return true;");
        ps.println("	}");
        ps.println();

        // hashCode
        ps.println("	@Override");
        ps.println("	public final int hashCode() {");
        Main._xdb_verify_(ps, "		");
        ps.println("		int _h_ = 0;");
        for (Variable var : variables) {
            var.hashCode(ps, "		");
        }
        ps.println("		return _h_;");
        ps.println("	}");
        ps.println();

        // toString
        ps.println("	@Override");
        ps.println("	public String toString() {");
        Main._xdb_verify_(ps, "		");
        ps.println("		StringBuilder _sb_ = new StringBuilder();");
        ps.println("		_sb_.append(\"(\");");
        boolean first = true;
        for (Variable var : variables) {
            if (false == first) {
                ps.println("		_sb_.append(\",\");");
            }
            var.toString(ps, "		");
            first = false;
        }
        ps.println("		_sb_.append(\")\");");
        ps.println("		return _sb_.toString();");
        ps.println("	}");
        ps.println();

        // newListenable
        ps.println("	@Override");
        ps.println("	public xdb.logs.Listenable newListenable() {");
        ps.println("		xdb.logs.ListenableBean lb = new xdb.logs.ListenableBean();");
        for (Variable var : variables) {
            ps.println("		lb.add(" + var.newListenable(this) + ");");
        }
        ps.println("		return lb;");
        ps.println("	}");
        ps.println();

        ////////////////////////////////////////////////
        // Const implement
        generateNestConstImeplement(ps, "	");
        ps.println();
        isData = true;
        generateNestDataImeplement(ps, "	");
        isData = false;
        ps.println("}");

        ps.close();
    }

    private void generateNestDataImplementCopyConstruct(PrintStream ps) {
        String cls = getName();
        ps.println("		Data(xbean." + cls + " _o1_) {");
        if (isAny()) {
            ps.println("			throw new UnsupportedOperationException();");
            ps.println("		}");
            ps.println();
            return;
        }

        ps.println("			if (_o1_ instanceof " + cls + ") {");
        ps.println("				assign((" + cls + ")_o1_);");
        ps.println("			} else if (_o1_ instanceof " + cls + ".Data) {");
        ps.println("				assign((" + cls + ".Data)_o1_);");
        ps.println("			} else if (_o1_ instanceof " + cls + ".Const) {");
        ps.println("				assign(((" + cls + ".Const)_o1_).nThis());");
        ps.println("			} else {");
        ps.println("				throw new UnsupportedOperationException();");
        ps.println("			}");
        ps.println("		}");
        ps.println();

        ps.println("		private void assign(" + cls + " _o_) {");
        for (Variable var : variables) {
            var.deepCopy(this, ps, "			");
        }
        ps.println("		}");
        ps.println();

        ps.println("		private void assign(" + cls + ".Data _o_) {");
        for (Variable var : variables) {
            var.deepCopy(this, ps, "			");
        }
        ps.println("		}");
        ps.println();
    }

    private void generateNestDataImeplement(PrintStream ps, String p) {
        String cls = getName();

        ps.println(p + "public static final class Data implements xbean." + cls + " {");

        for (Variable var : variables) {
            var.declare(ps, p + "	");
        }
        ps.println();

        ps.println(p + "	@Override");
        ps.println(p + "	public void _reset_unsafe_() {");
        for (Variable var : variables) {
            var.getVartype().print4reset(this, var, ps, p + "		");
        }
        ps.println(p + "	}");
        ps.println();

        ps.println(p + "	public Data() {");
        for (Variable var : variables) {
            var.construct(this, ps, p + "		");
        }
        ps.println(p + "	}");
        ps.println();
        generateNestDataImplementCopyConstruct(ps);

        if (Main.isUseJson()) {
            // marshal implement
            ps.println("	@Override");
            ps.println("	public final OctetsStream marshal(OctetsStream _os_) {");
            if (isAny()) {
                ps.println("		throw new UnsupportedOperationException();");
            } else {
                ps.println("		try {");
                ps.println("			StringWriter sw = new StringWriter();");
                ps.println("			JsonWriter _out_ = new JsonWriter(sw);");
                ps.println("			marshal(_out_);");
                ps.println("			_os_.marshalJson(sw.getBuffer().toString());");
                ps.println("			_out_.close();");
                ps.println("		} catch (Exception e) {");
                ps.println("			e.printStackTrace();");
                ps.println("		}");
                ps.println("		return _os_;");
            }
            ps.println("	}");
            ps.println();

            // marshal JsonWriter implement
            ps.println("	@Override");
            ps.println("	public final void marshal(JsonWriter _out_) throws Exception {");
            if (isAny()) {
                ps.println("		throw new UnsupportedOperationException();");
            } else {
                ps.println("		_out_.beginObject();");
                for (Variable var : variables) {
                    if (var.getVartype() instanceof XBean) {
                        ps.println("		_out_.name(\"" + var.getjsonname() + "\");");
                        ps.println("		" + var.getname() + ".marshal(_out_);");
                    } else {
                        ps.println("		if (" + var.defaultCheck() + ") {");
                        ps.println("			_out_.name(\"" + var.getjsonname() + "\");");
                        var.marshalJson(this, ps, "			", "this");
                        ps.println("		}");
                    }
                }
                ps.println("		_out_.endObject();");
            }
            ps.println("	}");
            ps.println();

            // unmarshal implement
            ps.println("	@Override");
            ps.println("	public final OctetsStream unmarshal(OctetsStream _os_) {");
            if (isAny()) {
                ps.println("		throw new UnsupportedOperationException();");
            } else {
                ps.println("		try {");
                ps.println("			JsonReader jr = new JsonReader(new StringReader(_os_.unmarshalJson()));");
                ps.println("			unmarshal(jr);");
                ps.println("			jr.close();");
                ps.println("		} catch (Exception e) {");
                ps.println("			e.printStackTrace();");
                ps.println("		}");
                ps.println("		return _os_;");
            }
            ps.println("	}");
            ps.println();

            // unmarshal JsonReader implement
            ps.println("	@Override");
            ps.println("	public final void unmarshal(JsonReader _in_) throws Exception {");
            if (isAny()) {
                ps.println("		throw new UnsupportedOperationException();");
            } else {
                ps.println("		_in_.beginObject();");
                ps.println("		while (_in_.hasNext()) {");
                ps.println("			switch (_in_.nextName()) {");
                for (Variable var : variables) {
                    ps.println("				case \"" + var.getjsonname() + "\":");
                    if (var.getVartype() instanceof XBean) {
                        ps.println("					" + var.getname() + ".unmarshal(_in_);");
                    } else {
                        var.unmarshalJson(this, ps, "					", "this");
                    }
                    ps.println("					break;");
                }
                ps.println("				default:");
                ps.println("					_in_.skipValue();");
                ps.println("					break;");
                ps.println("			}");
                ps.println("		}");
                ps.println("		_in_.endObject();");
            }
            ps.println("	}");
            ps.println();
        } else {
            // marshal implement
            ps.println(p + "	@Override");
            ps.println(p + "	public final OctetsStream marshal(OctetsStream _os_) {");
            if (isAny()) {
                ps.println(p + "		throw new UnsupportedOperationException();");
            } else {
                ps.println(p + "		_os_.compact_uint32(" + variables.size() + ");");
                for (Variable var : variables) {
                    var.marshal(this, ps, p + "		");
                }
                ps.println(p + "		return _os_;");
            }
            ps.println(p + "	}");
            ps.println();

            // unmarshal implement
            ps.println(p + "	@Override");
            ps.println(p + "	public final OctetsStream unmarshal(OctetsStream _os_) throws MarshalException {");
            if (isAny()) {
                ps.println(p + "		throw new UnsupportedOperationException();");
            } else {
                ps.println("		int _varIndex = 0;");
                ps.println("		int _varSize = _os_.uncompact_uint32();");
                for (Variable var : variables) {
                    ps.println("		if (_varIndex++ < _varSize) {");
                    var.unmarshal(this, ps, p + "		");
                    ps.println("		}");
                }
                ps.println(p + "		return _os_;");
            }
            ps.println(p + "	}");
            ps.println();
        }

        // copy toData toBean
        ps.println(p + "	@Override");
        ps.println(p + "	public xbean." + cls + " copy() {");
        ps.println(p + "		return new Data(this);");
        ps.println(p + "	}");
        ps.println();
        ps.println(p + "	@Override");
        ps.println(p + "	public xbean." + cls + " toData() {");
        if (isAny()) {
            ps.println(p + "		return new Data(this);");
        } else {
            ps.println(p + "		xbean.__." + cls + ".Data tmp = (Data) xbean.Pod.new" + cls + "Data();");
            ps.println(p + "		tmp.assign(this);");
            ps.println(p + "		return tmp;");
        }
        ps.println(p + "	}");
        ps.println();
        ps.println(p + "	public xbean." + cls + " toBean() {");
        ps.println(p + "		return new " + cls + "(this, null, null);");
        ps.println(p + "	}");
        ps.println();
        ps.println(p + "	@Override");
        ps.println(p + "	public xbean." + cls + " toDataIf() {");
        ps.println(p + "		return this;");
        ps.println(p + "	}");
        ps.println();
        ps.println(p + "	public xbean." + cls + " toBeanIf() {");
        ps.println(p + "		return new " + cls + "(this, null, null);");
        ps.println(p + "	}");
        ps.println();

        ps.println(p + "	// xdb.Bean interface. Data Unsupported");
        ps.println(p + "	public boolean xdbManaged() {");
        ps.println(p + "		throw new UnsupportedOperationException();");
        ps.println(p + "	}");
        ps.println(p + "	public xdb.Bean xdbParent() {");
        ps.println(p + "		throw new UnsupportedOperationException();");
        ps.println(p + "	}");
        ps.println(p + "	public String xdbVarname()  {");
        ps.println(p + "		throw new UnsupportedOperationException();");
        ps.println(p + "	}");
        ps.println(p + "	public Long xdbObjId() {");
        ps.println(p + "		throw new UnsupportedOperationException();");
        ps.println(p + "	}");
        ps.println(p + "	public xdb.Bean toConst() {	");
        ps.println(p + "		throw new UnsupportedOperationException();");
        ps.println(p + "	}");
        ps.println(p + "	public boolean isConst() {");
        ps.println(p + "		return false;");
        ps.println(p + "	}");
        ps.println(p + "	public boolean isData() {");
        ps.println(p + "		return true;");
        ps.println(p + "	}");
        ps.println();

        // getter setter
        for (Variable var : variables) {
            var.getterData(this, ps, p + "	");
        }
        for (Variable var : variables) {
            var.setterData(this, ps, p + "	");
        }

        // equals
        ps.println(p + "	@Override");
        ps.println(p + "	public final boolean equals(Object _o1_) {");
        ps.println(p + "		if (!(_o1_ instanceof " + cls + ".Data)) {");
        ps.println(p + "			return false;");
        ps.println(p + "		}");
        ps.println(p + "		" + cls + ".Data _o_ = (" + cls + ".Data) _o1_;");
        for (Variable var : variables) {
            var.equals(ps, "			");
        }
        ps.println(p + "		return true;");
        ps.println(p + "	}");
        ps.println();

        // hashCode
        ps.println(p + "	@Override");
        ps.println(p + "	public final int hashCode() {");
        ps.println(p + "		int _h_ = 0;");
        for (Variable var : variables) {
            var.hashCode(ps, p + "		");
        }
        ps.println(p + "		return _h_;");
        ps.println(p + "	}");
        ps.println();

        // toString
        ps.println(p + "	@Override");
        ps.println(p + "	public String toString() {");
        ps.println(p + "		StringBuilder _sb_ = new StringBuilder();");
        ps.println(p + "		_sb_.append(\"(\");");
        boolean first = true;
        for (Variable var : variables) {
            if (false == first) {
                ps.println(p + "		_sb_.append(\",\");");
            }
            var.toString(ps, p + "		");
            first = false;
        }
        ps.println(p + "		_sb_.append(\")\");");
        ps.println(p + "		return _sb_.toString();");
        ps.println(p + "	}");
        ps.println();
        ps.println(p + "}");
    }

    private void generateNestConstImeplement(PrintStream ps, String p) {
        String N = getName();
        String Nthis = N + ".this";

        ps.println(p + "private class Const implements xbean." + N + " {");
        // copy
        ps.println(p + "	" + N + " nThis() {");
        ps.println(p + "		return " + Nthis + ";");
        ps.println(p + "	}");
        ps.println();
        ps.println(p + "	@Override");
        ps.println(p + "	public void _reset_unsafe_() {");
        ps.println(p + "		throw new UnsupportedOperationException();");
        ps.println(p + "	}");
        ps.println();

        ps.println(p + "	@Override");
        ps.println(p + "	public xbean." + N + " copy() {");
        ps.println(p + "		return " + Nthis + ".copy();");
        ps.println(p + "	}");
        ps.println();
        ps.println(p + "	@Override");
        ps.println(p + "	public xbean." + N + " toData() {");
        ps.println(p + "		return " + Nthis + ".toData();");
        ps.println(p + "	}");
        ps.println();
        ps.println(p + "	public xbean." + N + " toBean() {");
        ps.println(p + "		return " + Nthis + ".toBean();");
        ps.println(p + "	}");
        ps.println();
        ps.println(p + "	@Override");
        ps.println(p + "	public xbean." + N + " toDataIf() {");
        ps.println(p + "		return " + Nthis + ".toDataIf();");
        ps.println(p + "	}");
        ps.println();
        ps.println(p + "	public xbean." + N + " toBeanIf() {");
        ps.println(p + "		return " + Nthis + ".toBeanIf();");
        ps.println(p + "	}");
        ps.println();

        // getter setter
        for (Variable var : variables) {
            var.getterConst(this, ps, p + "	");
        }
        for (Variable var : variables) {
            var.setterConst(this, ps, p + "	");
        }
        // const
        ps.println(p + "	@Override");
        ps.println(p + "	public xdb.Bean toConst() {");
        Main._xdb_verify_(ps, p + "		");
        ps.println(p + "		return this;");
        ps.println(p + "	}");
        ps.println();
        // const
        ps.println(p + "	@Override");
        ps.println(p + "	public boolean isConst() {");
        Main._xdb_verify_(ps, p + "		");
        ps.println(p + "		return true;");
        ps.println(p + "	}");
        ps.println();
        ps.println(p + "	@Override");
        ps.println(p + "	public boolean isData() {");
        ps.println(p + "		return " + Nthis + ".isData();");
        ps.println(p + "	}");
        ps.println();
        ps.println(p + "	@Override");
        ps.println(p + "	public OctetsStream marshal(OctetsStream _os_) {");
        ps.println(p + "		return " + Nthis + ".marshal(_os_);");
        ps.println(p + "	}");
        ps.println();
        ps.println(p + "	@Override");
        ps.println(p + "	public OctetsStream unmarshal(OctetsStream arg0) throws MarshalException {");
        Main._xdb_verify_(ps, p + "		");
        ps.println(p + "		throw new UnsupportedOperationException();");
        ps.println(p + "	}");
        ps.println();
        if (Main.isUseJson()) {
            ps.println(p + "	@Override");
            ps.println(p + "	public void marshal(JsonWriter _out_) throws Exception {");
            ps.println(p + "		" + Nthis + ".marshal(_out_);");
            ps.println(p + "	}");
            ps.println();
            ps.println(p + "	@Override");
            ps.println(p + "	public void unmarshal(JsonReader _in_) throws Exception {");
            Main._xdb_verify_(ps, p + "		");
            ps.println(p + "		throw new UnsupportedOperationException();");
            ps.println(p + "	}");
            ps.println();
        }
        ps.println(p + "	@Override");
        ps.println(p + "	public xdb.Bean xdbParent() {");
        ps.println(p + "		return " + Nthis + ".xdbParent();");
        ps.println(p + "	}");
        ps.println();
        ps.println(p + "	@Override");
        ps.println(p + "	public boolean xdbManaged() {");
        ps.println(p + "		return " + Nthis + ".xdbManaged();");
        ps.println(p + "	}");
        ps.println();
        ps.println(p + "	@Override");
        ps.println(p + "	public String xdbVarname() {");
        ps.println(p + "		return " + Nthis + ".xdbVarname();");
        ps.println(p + "	}");
        ps.println();
        ps.println(p + "	@Override");
        ps.println(p + "	public Long xdbObjId() {");
        ps.println(p + "		return " + Nthis + ".xdbObjId();");
        ps.println(p + "	}");
        ps.println();
        ps.println(p + "	@Override");
        ps.println(p + "	public boolean equals(Object obj) {");
        ps.println(p + "		return " + Nthis + ".equals(obj);");
        ps.println(p + "	}");
        ps.println();
        ps.println(p + "	@Override");
        ps.println(p + "	public int hashCode() {");
        ps.println(p + "		return " + Nthis + ".hashCode();");
        ps.println(p + "	}");
        ps.println();
        ps.println(p + "	@Override");
        ps.println(p + "	public String toString() {");
        ps.println(p + "		return " + Nthis + ".toString();");
        ps.println(p + "	}");
        ps.println();
        ps.println(p + "}");
    }

    private void generateInterface() {
        String classname = getName();
        PrintStream ps = Main.openBeanFile(classname);

        ps.println();
        ps.println("package xbean;");
        ps.println();
        if (Main.isUseJson()) {
            ps.println("import com.google.gson.stream.JsonReader;");
            ps.println("import com.google.gson.stream.JsonWriter;");
        }
        ps.println();
        ps.println("public interface " + classname + " extends xdb.Bean {");

        ps.println("	public " + classname + " copy(); // deep clone");
        ps.println("	public " + classname + " toData(); // a Data instance");
        ps.println("	public " + classname + " toBean(); // a Bean instance");
        ps.println("	public " + classname + " toDataIf(); // a Data instance If need. else return this");
        ps.println("	public " + classname + " toBeanIf(); // a Bean instance If need. else return this");
        if (Main.isUseJson()) {
            ps.println("	void marshal(JsonWriter _out_) throws Exception;");
            ps.println("	void unmarshal(JsonReader _in_) throws Exception;");
        }
        ps.println();
        // enums
        for (Enum e : enums) {
            e.print(ps, "	");
        }
        if (!enums.isEmpty()) {
            ps.println();
        }

        // getter
        for (Variable var : variables) {
            var.getterInterface(this, ps, "	");
        }
        ps.println();

        // setter
        for (Variable var : variables) {
            var.setterInterface(this, ps, "	");
        }

        ps.println("}");

        ps.close();
    }

    void make() {
        Type.setCurrentBean(this);
        generateInterface();
        generateImplement();
        Type.setCurrentBean(null);
    }

    public int getMaxJsonName() {
        return maxjsonname;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getTypeName() {
        return "xbean." + getName();
    }

    @Override
    public String getBoxingName() {
        return getTypeName();
    }

    @Override
    public void construct(XBean bean, Variable var, PrintStream ps, String prefix) {
        String varname = var.getname();
        if (bean.isData()) {
            ps.println(prefix + varname + " = new " + getName() + ".Data();");
        } else {
            ps.println(prefix + varname + " = new " + getName() + "(0, this, " + Main.quote(varname) + ");");
        }
    }

    @Override
    public String defaultCheck(Variable var) {
        return var.getname() + " != null";
    }

    @Override
    public void print4reset(XBean bean, Variable var, PrintStream ps, String prefix) {
        String varname = var.getname();
        ps.println(prefix + varname + "._reset_unsafe_();");
    }

    @Override
    public String deepCopy(boolean isData, Variable var, String fullvarname) {
        if (isData) {
            return "new " + getName() + ".Data(" + fullvarname + ")";
        }
        return "new " + getName() + "(" + fullvarname + ", this, " + Main.quote(var.getname()) + ")";
    }

    @Override
    public void deepCopy(boolean isData, Variable var, PrintStream ps, String prefix) {
        if (isData) {
            ps.println(prefix + var.getname() + " = new " + getName() + ".Data(_o_." + var.getname() + ");");
        } else {
            ps.println(prefix + var.getname() + " = new " + getName() + "(_o_." + var.getname() + ", this, " + Main.quote(var.getname()) + ");");
        }
    }

    @Override
    public int deepcopy4cache(PrintStream ps, String pf) {
        if (ps == null) {
            return 1; // 仅仅判断是否需要生成
        }

        ps.println(pf + "public static " + this.getGetterName() + " deepcopy(" + this.getGetterName() + " _o_) {");
        ps.println(pf + "	return _o_.toDataIf();");
        ps.println(pf + "}");
        return 1;
    }

    @Override
    public boolean isConstant() {
        return false;
    }

    @Override
    public void marshal(XBean bean, Variable var, PrintStream ps, String prefix, String localvarname) {
        ps.println(prefix + localvarname + ".marshal(_os_);");
    }

    @Override
    public void unmarshal(XBean bean, Variable var, PrintStream ps, String prefix, String localvarname) {
        ps.println(prefix + localvarname + ".unmarshal(_os_);");
    }

    @Override
    public void marshalJson(XBean bean, Variable var, PrintStream ps, String prefix, String localvarname) {
        ps.println(prefix + "_out_.beginObject();");
        for (Variable tmpvar : variables) {
            ps.println(prefix + "_out_.name(\"" + tmpvar.getjsonname() + "\");");
            if (tmpvar.getVartype() instanceof XBean) {
                ps.println(prefix + tmpvar.getname() + ".marshal(_out_);");
            } else {
                tmpvar.marshalJson(this, ps, prefix, localvarname);
            }
        }
        ps.println(prefix + "_out_.endObject();");
    }

    @Override
    public void unmarshalJson(XBean bean, Variable var, PrintStream ps, String prefix, String localvarname) {
        ps.println(prefix + "_in_.beginObject();");
        ps.println(prefix + "while (_in_.hasNext()) {");
        ps.println(prefix + "	switch (_in_.nextName()) {");
        for (Variable tmpvar : variables) {
            ps.println(prefix + "		case \"" + tmpvar.getjsonname() + "\":");
            if (tmpvar.getVartype() instanceof XBean) {
                ps.println(prefix + tmpvar.getname() + ".unmarshal(_in_);");
            } else {
                tmpvar.unmarshalJson(this, ps, prefix, localvarname);
            }
            ps.println(prefix + "			break;");
        }
        ps.println(prefix + "		default:");
        ps.println(prefix + "			_in_.skipValue();");
        ps.println(prefix + "			break;");
        ps.println(prefix + "	}");
        ps.println(prefix + "}");
        ps.println(prefix + "_in_.endObject();");
    }

    @Override
    public void unmarshalJsonKey(XBean bean, Variable var, PrintStream ps, String prefix, String localvarname) {
        throw new UnsupportedOperationException();
    }

    @Override
    public String defineSetParent(XBean bean, Variable var, String definename) {
        if (bean.isData()) {
            return defineNoParent(definename);
        }

        return getTypeName() + " " + definename + " = new " + getName() + "(0, this, " + Main.quote(var.getname()) + ");";
    }

    @Override
    public String defineNoParent(String varname) {
        if (Type.getCurrentBean() != null && Type.getCurrentBean().isData()) {
            return getTypeName() + " " + varname + " = xbean.Pod.new" + getName() + "Data();";
        }
        return getTypeName() + " " + varname + " = xbean.Pod.new" + getName() + "();";
    }

    @Override
    public String hashCode(String varname) {
        return varname + ".hashCode()";
    }

    @Override
    public String notEquals(String varname) {
        return "!" + varname + ".equals(_o_." + varname + ")";
    }

    @Override
    public void getterInterface(XBean bean, Variable var, PrintStream ps, String prefix) {
        ps.println(prefix + "public xbean." + name + " get" + var.getName() + "(); // " + var.getComment());
    }

    @Override
    public void getter(XBean bean, Variable var, PrintStream ps, String prefix) {
        ps.println(prefix + "@Override");
        ps.println(prefix + "public xbean." + name + " get" + var.getName() + "() { // " + var.getComment());
        Main._xdb_verify_(ps, prefix + "	");
        ps.println(prefix + "	return " + var.getname() + ";");
        ps.println(prefix + "}");
        ps.println();
    }

    @Override
    public void getterConst(XBean bean, Variable var, PrintStream ps, String prefix) {
        ps.println(prefix + "@Override");
        ps.println(prefix + "public xbean." + name + " get" + var.getName() + "() { // " + var.getComment());
        Main._xdb_verify_(ps, prefix + "	");
        ps.println(prefix + "	return xdb.Consts.toConst(" + var.getname() + ");");
        ps.println(prefix + "}");
        ps.println();
    }

    @Override
    public void getterData(XBean bean, Variable var, PrintStream ps, String prefix) {
        ps.println(prefix + "@Override");
        ps.println(prefix + "public xbean." + name + " get" + var.getName() + "() { // " + var.getComment());
        ps.println(prefix + "	return " + var.getname() + ";");
        ps.println(prefix + "}");
        ps.println();
    }

    @Override
    public void setter(XBean bean, Variable var, PrintStream ps, String prefix) {
        // unsupported
    }

    @Override
    public void setterConst(XBean bean, Variable var, PrintStream ps, String prefix) {
        // unsupported
    }

    @Override
    public void setterInterface(XBean bean, Variable var, PrintStream ps, String prefix) {
        // unsupported
    }

    @Override
    public void setterData(XBean bean, Variable var, PrintStream ps, String prefix) {
        // unsupported
    }

    @Override
    public String newListenable(XBean bean, Variable var) {
        return "new xdb.logs.ListenableChanged().setVarName(" + Main.quote(var.getname()) + ")";
        // 使用下面的实现，可以实现递归层次日志报告。
        // return var.getname() + ".newListenable().setVarName(" + Main.quote(var.getname()) + ")";
    }

    @Override
    public void printTableSelectMethod(PrintStream ps, String K) {
        final String V = this.getBoxingName();
        if (false == this.isAny()) {
            // any 不支持拷贝操作，无法支持 select。生成代码不会发生编译错误，执行时报错。干脆不生成了。
            ps.println("	public static " + V + " select(" + K + " key) throws Exception {");
            ps.println("		return getTable().select(key, new xdb.TField<" + V + ", " + V + ">() {");
            ps.println("			public " + V + " get(" + V + " v) {");
            ps.println("				return v.toData();");
            ps.println("			}");
            ps.println("		});");
            ps.println("	}");
            ps.println();

            // 添加selectWithoutCache接口，对某些只读的数据不添加缓存，避免缓存过大
            ps.println("	public static " + V + " selectWithoutCache(" + K + " key) throws Exception {");
            ps.println("		return getTable().selectWithoutCache(key, new xdb.TField<" + V + ", " + V + ">() {");
            ps.println("			public " + V + " get(" + V + " v) {");
            ps.println("				return v.toData();");
            ps.println("			}");
            ps.println("		});");
            ps.println("	}");
            ps.println();

            // 添加selectWithoutCache4Rollback接口，用来单用户回档
            ps.println("	public static " + V + " selectWithoutCache4Rollback(" + K + " key) throws Exception {");
            ps.println("		return getTable().selectWithoutCache4Rollback(key, new xdb.TField<" + V + ", " + V + ">() {");
            ps.println("			public " + V + " get(" + V + " v) {");
            ps.println("				return v.toData();");
            ps.println("			}");
            ps.println("		});");
            ps.println("	}");
            ps.println();
        }
        // any 类型的xbean里面包含的constant字段仍然可以支持select.
        for (Variable var : variables) {
            var.printSelect(ps, K, V);
        }
    }

    public void printMeta(PrintStream ps, String prefix) {
        ps.println(prefix + "Bean bean = new Bean(" + getMaxJsonName() + ", " + Main.quote(getName()) + ", " + isAny() + ", " + isConstant() + ");");
        for (Variable var : this.variables) {
            ps.print(prefix + "super.addVariableFor(bean");
            var.printMetaData(ps, "");
            ps.println(");");
        }
        ps.println(prefix + "super.addBean(bean);");
    }

    public void setHasModify() {
        hasModify = true;
    }

    public boolean hasModify() {
        return hasModify;
    }

    public Variable getVariable(String name) {
        for (Variable var : variables) {
            if (var.getname().equals(name)) {
                return var;
            }
        }
        return null;
    }

    public List<Variable> getVariables() {
        return variables;
    }

    public void skipVarMarshal(Variable var) {
        //System.out.println("Generate marshal: Skip var: " + this.name + "." + var.getname() );
        return;
    }

    @Override
    public void depends(Set<Type> types) {
        if (Type.addDepend(types, this)) {
            for (Variable var : variables) {
                var.getVartype().depends(types);
            }
        }
    }

    /**
     * add by cjc .
     * 梦诛那需要把只带enums的xbean放开.这样
     */
    public boolean isEnumsOnly() {
        return variables.size() == 0 && enums.size() > 0;
    }
}
